#include "stdinclude.h"
#include "input/InputSys.h"
#include "oisInputMgr.h"

#undef min

using namespace OIS;

bool appRunning=true;

const char * InputMgrImpl::g_DeviceType[6] = {"OISUnknown", "OISKeyboard", "OISMouse", "OISJoyStick", "OISTablet", "OISOther"};


InputMgrImpl::InputMgrImpl(InputSys* const inputSys) :	
	inputSys(inputSys),
	_InputManager(NULL),
	_kb(NULL),
	_m(NULL)
{
}


InputMgrImpl::~InputMgrImpl(void)
{
}


void InputMgrImpl::init(const PlatformData& data)
{
	ParamList pl;
	std::ostringstream oisInfo;

	try
	{
		unsigned wnd_sz_x=0;
		unsigned wnd_sz_y=0;

#ifdef _WIN32
		//window
		std::string wnd_str = StringTools::format("%d",	data.hWnd);
		pl.insert(std::make_pair( std::string("WINDOW"), wnd_str ));

		//window size
		RECT rect;
		::GetClientRect(data.hWnd, &rect);
		wnd_sz_x = rect.right- rect.left;
		wnd_sz_y = rect.bottom - rect.top;

		//non esclusive input	
		pl.insert(std::make_pair(std::string("w32_mouse"), std::string("DISCL_FOREGROUND" )));
		pl.insert(std::make_pair(std::string("w32_mouse"), std::string("DISCL_NONEXCLUSIVE")));
		pl.insert(std::make_pair(std::string("w32_keyboard"), std::string("DISCL_FOREGROUND")));
		pl.insert(std::make_pair(std::string("w32_keyboard"), std::string("DISCL_NONEXCLUSIVE")));
#else
	#error "Not supported platform!"
#endif

		//This never returns null.. it will raise an exception on errors
		_InputManager = InputManager::createInputSystem(pl);

		//Lets enable all addons that were compiled in:
		_InputManager->enableAddOnFactory(InputManager::AddOn_All);

		//Print debugging information
		unsigned int v = _InputManager->getVersionNumber();		
		
		
		oisInfo << "OIS Version: " << (v>>16 ) << "." << ((v>>8) & 0x000000FF) << "." << (v & 0x000000FF)
			<< ";\tRelease Name: " << _InputManager->getVersionName()
			<< ";\tManager: " << _InputManager->inputSystemName()
			<< ";\tTotal Keyboards: " << _InputManager->getNumberOfDevices(OISKeyboard)
			<< ";\tTotal Mice: " << _InputManager->getNumberOfDevices(OISMouse)
			<< ";\tTotal JoySticks: " << _InputManager->getNumberOfDevices(OISJoyStick)<<std::endl;

		//_dbgout( ("OIS Info: [ "+ oisInfo.str() + " ]\n").c_str());
	
		//List all devices
		DeviceList list = _InputManager->listFreeDevices();	
		for( DeviceList::iterator i = list.begin(); i != list.end(); ++i )
		{
			(oisInfo << "\tDevice: " << g_DeviceType[i->first] << " Vendor: " << i->second << std::endl) ;
			//_dbgout(oisInfo.str().c_str() );
		}
		
		//KEYBOARD
		if( _InputManager->getNumberOfDevices(OIS::OISKeyboard) > 0) {
			_kb = static_cast<OIS::Keyboard*>(_InputManager->createInputObject( OISKeyboard, true ));
			_kb->setEventCallback( this );
		}

		//MOUSE
		if( _InputManager->getNumberOfDevices(OIS::OISMouse) > 0) {
			_m = static_cast<OIS::Mouse*>(_InputManager->createInputObject( OISMouse, true ));
			_m->setEventCallback( this );
			const MouseState &ms = _m->getMouseState();
			ms.width = wnd_sz_x;
			ms.height = wnd_sz_y;
		}

		//JOYSTICK	
		//This demo uses at most 10 joysticks - use old way to create (i.e. disregard vendor)
		int numSticks = std::min(_InputManager->getNumberOfDevices(OISJoyStick), 10);	 
		_joys.resize(numSticks);
		for( int i = 0; i < numSticks; ++i )
		{
			_joys[i] = static_cast<OIS::JoyStick*>(_InputManager->createInputObject( OISJoyStick, true ));
			_joys[i]->setEventCallback( this );
			
			oisInfo << "\n\nCreating Joystick " << (i + 1)
				<< "\n\tAxes: " << _joys[i]->getNumberOfComponents(OIS_Axis)
				<< "\n\tSliders: " << _joys[i]->getNumberOfComponents(OIS_Slider)
				<< "\n\tPOV/HATs: " << _joys[i]->getNumberOfComponents(OIS_POV)
				<< "\n\tButtons: " << _joys[i]->getNumberOfComponents(OIS_Button)
				<< "\n\tVector3: " << _joys[i]->getNumberOfComponents(OIS_Vector3) << std::endl;

			
			//_dbgout(oisInfo.str().c_str() );
		}

		this->inputSys->_info = oisInfo.str(); //INIT OK
	}
	catch(OIS::Exception &ex)
	{
		//oisInfo.str("");		
		oisInfo << "\nException raised on ois setup creation: " << ex.what() << std::endl;
		this->inputSys->_info = oisInfo.str();
		throw InitException(ex.what());		
	}

}


void InputMgrImpl::shutdown()
{
	//Destroying the manager will cleanup unfreed devices
	if( _InputManager )
	{
		if(_m)
		{
			_InputManager->destroyInputObject( _m );
			_m=0;		
		}
		if(_kb)
		{
			_InputManager->destroyInputObject( _kb );
			_kb=0;
		}


		unsigned jcnt =_joys.size();
		if(jcnt>0)
		{
			for( unsigned i = 0; i < jcnt ; ++i )
			{
				if( _joys[i]!=NULL )
				{
					_InputManager->destroyInputObject( _joys[i] );
				}
			}
			_joys.clear();
		}

		InputManager::destroyInputSystem(_InputManager);
		_InputManager=0;		
	}

	this->inputSys->_info = "shutdown";
}


void InputMgrImpl::captureInput()
{
	if( _kb )
	{
		_kb->capture();
		//if( !_kb->buffered() )			handleNonBufferedKeys();
	}

	if( _m )
	{
		_m->capture();
		//if( !_m->buffered() )			handleNonBufferedMouse();
	}

	unsigned jcnt = _joys.size();
	if(jcnt>0)
	{
		for( unsigned i = 0; i < jcnt ; ++i )
		{
			if( _joys[i]!=NULL )
			{
				_joys[i]->capture();
				//if( !_joys[i]->buffered() )				handleNonBufferedJoy( _joys[i] );
			}
		}
	}
}

String InputMgrImpl::getAsString(InputKeyCodeEnum keyCodeEnum)
{
	if(_kb)
	{
		OIS::KeyCode code = (OIS::KeyCode) keyCodeEnum;
		return _kb->getAsString(code);
	}
	return "";
}
//////////////////////////////////////////////



bool InputMgrImpl::keyPressed( const OIS::KeyEvent &arg ) 
{
#ifdef _DEBUG
	std::ostringstream oisInfo("");		 
	oisInfo << " KeyPressed {" << arg.key
		<< ", " << (((OIS::Keyboard*)(arg.device))->getAsString(arg.key))
		<< "} || Character (" << ((char)arg.text) << ")" ;	//	
	_dbgout((oisInfo.str()+"\n").c_str());	
#endif

	//signal
	inputSys->signalKeyPressed( (InputKeyCodeEnum) arg.key);

	return true;
}

bool InputMgrImpl::keyReleased( const OIS::KeyEvent &arg ) {		

//#ifdef _DEBUG
//	std::ostringstream oisInfo("");		 	
//	oisInfo.str("");
//	oisInfo << "KeyReleased {" << ((OIS::Keyboard*)(arg.device))->getAsString(arg.key) << "}\n";
//	_dbgout((oisInfo.str()+"\n").c_str());
//#endif

	//signal
	inputSys->signalKeyReleased( (InputKeyCodeEnum) arg.key);

	return true;
}

bool InputMgrImpl::mouseMoved( const OIS::MouseEvent &arg ) {
	const OIS::MouseState& s = arg.state;
//#ifdef _DEBUG
//	std::ostringstream oisInfo("");		 	
//	oisInfo << "\nMouseMoved: Abs("
//				<< s.X.abs << ", " << s.Y.abs << ", " << s.Z.abs << ") Rel("
//				<< s.X.rel << ", " << s.Y.rel << ", " << s.Z.rel << ")";
//	_dbgout((oisInfo.str()+"\n").c_str());
//#endif

	//signal 
	inputSys->signalMouseMoved(convert(s), InputMouseButtonEnum::MB_None);

	return true;
}

bool InputMgrImpl::mousePressed( const OIS::MouseEvent &arg, OIS::MouseButtonID id ) {
	const OIS::MouseState& s = arg.state;
#ifdef _DEBUG
	std::ostringstream oisInfo("");		 	
	oisInfo << "\nMouse button #" << id << " pressed. Abs("
				<< s.X.abs << ", " << s.Y.abs << ", " << s.Z.abs << ") Rel("
				<< s.X.rel << ", " << s.Y.rel << ", " << s.Z.rel << ")";
	_dbgout((oisInfo.str()+"\n").c_str());
#endif

	//signal 
	inputSys->signalMousePressed(convert(s), (InputMouseButtonEnum) id);

	return true;
}

bool InputMgrImpl::mouseReleased( const OIS::MouseEvent &arg, OIS::MouseButtonID id ) {
	const OIS::MouseState& s = arg.state;
	/*oisInfo.str("");
	oisInfo << "\nMouse button #" << id << " released. Abs("
				<< s.X.abs << ", " << s.Y.abs << ", " << s.Z.abs << ") Rel("
				<< s.X.rel << ", " << s.Y.rel << ", " << s.Z.rel << ")";
	_dbgout((oisInfo.str()+"\n").c_str());*/

	//signal 
	inputSys->signalMousePressed(convert(s), (InputMouseButtonEnum) id);

	return true;
}

bool InputMgrImpl::buttonPressed( const OIS::JoyStickEvent &arg, int button ) {
#ifdef _DEBUG
	std::ostringstream oisInfo("");		 
	oisInfo << std::endl << arg.device->vendor() << ". Button Pressed # " << button;
	_dbgout((oisInfo.str()+"\n").c_str());
#endif

	//signal 
	inputSys->signalJoyButtonPressed(convert(arg.state), button);

	return true;
}

bool InputMgrImpl::buttonReleased( const OIS::JoyStickEvent &arg, int button ) {
	/*oisInfo.str("");
	oisInfo << std::endl << arg.device->vendor() << ". Button Released # " << button;
	_dbgout((oisInfo.str()+"\n").c_str());*/

	//signal 
	inputSys->signalJoyButtonReleased(convert(arg.state), button);

	return true;
}

bool InputMgrImpl::axisMoved( const OIS::JoyStickEvent &arg, int axis )
{
	//Provide a little dead zone
	if( arg.state.mAxes[axis].abs > 2500 || arg.state.mAxes[axis].abs < -2500 )
	{
#ifdef _DEBUG
		std::ostringstream oisInfo("");		 	
		oisInfo << std::endl << arg.device->vendor() << ". Axis # " << axis << " Value: " << arg.state.mAxes[axis].abs;
		_dbgout((oisInfo.str()+"\n").c_str());
#endif
		//signal 
		inputSys->signalJoyAxisMoved(convert(arg.state), axis);

	}
	return true;
}

bool InputMgrImpl::povMoved( const OIS::JoyStickEvent &arg, int pov )
{
#ifdef _DEBUG
	std::ostringstream oisInfo("");	 	
	oisInfo << std::endl << arg.device->vendor() << ". POV" << pov << " ";

	if( arg.state.mPOV[pov].direction & OIS::Pov::North ) //Going up
		oisInfo << "North";
	else if( arg.state.mPOV[pov].direction & OIS::Pov::South ) //Going down
		oisInfo << "South";

	if( arg.state.mPOV[pov].direction & OIS::Pov::East ) //Going right
		oisInfo << "East";
	else if( arg.state.mPOV[pov].direction & OIS::Pov::West ) //Going left
		oisInfo << "West";

	if( arg.state.mPOV[pov].direction == OIS::Pov::Centered ) //stopped/centered out
		oisInfo << "Centered";

	_dbgout((oisInfo.str()+"\n").c_str());
#endif

	//signal 
	inputSys->signalJoyAxisMoved(convert(arg.state), pov);

	return true;
}

bool InputMgrImpl::vector3Moved( const OIS::JoyStickEvent &arg, int index)
{
#ifdef _DEBUG
	std::ostringstream oisInfo("");	 	
	oisInfo.precision(2);
	oisInfo.flags(std::ios::fixed | std::ios::right);
	oisInfo << std::endl << arg.device->vendor() << ". Orientation # " << index 
		<< " X Value: " << arg.state.mVectors[index].x
		<< " Y Value: " << arg.state.mVectors[index].y
		<< " Z Value: " << arg.state.mVectors[index].z;
	oisInfo.precision();
	oisInfo.flags();

	_dbgout((oisInfo.str()+"\n").c_str());
#endif

	//TODO signal 
	//inputSys->signalJoyAxisMoved(convert(arg.state), pov);

	return true;
}





///////////////////////////////
//conversins
static InputAxis convert(const OIS::Axis& oisAxis)
{
	InputAxis axis;
	axis.abs= oisAxis.abs;
	axis.absOnly = oisAxis.absOnly;
	axis.rel = oisAxis.rel;
	return axis;
}
static std::vector<InputAxis> convert(const std::vector<OIS::Axis>& oisAxisVector)
{
	std::vector<InputAxis> axisVector;
	for(std::vector<OIS::Axis>::const_iterator i = oisAxisVector.begin(), e = oisAxisVector.end(); i != e; ++i )
	{
		axisVector.push_back( ::convert(*i) );
	}
	return axisVector;
}

InputMouseState InputMgrImpl::convert(const OIS::MouseState& oisMouseState)const
{
	InputMouseState mouseState;
	mouseState.buttons = oisMouseState.buttons;
	mouseState.height = oisMouseState.height;
	mouseState.width = oisMouseState.width;

	mouseState.X = ::convert(oisMouseState.X);
	mouseState.Y = ::convert(oisMouseState.Y);
	mouseState.Z = ::convert(oisMouseState.Z);	

	return mouseState;
}

InputJoyStickState InputMgrImpl::convert(const OIS::JoyStickState& oisJoyState)const
{
	InputJoyStickState joyState;
	
	joyState.mAxes= ::convert( oisJoyState.mAxes );
	joyState.mButtons = oisJoyState.mButtons;
	
	return joyState;
}
///////////////////////////////